﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace LGE
{
    public abstract class EngineObject
    {
        public Texture2D sprite;
        public Vector2 origin;
        public float scale;
        public Rectangle sourceRectangle = new Rectangle(0, 0, 0, 0);
        public Rectangle HitBox { 
            get { return new Rectangle(hitBox.X + (int)position.X, hitBox.Y + (int)position.Y, hitBox.Width, hitBox.Height); } 
            set { hitBox = value; hitBox.X -= hitBox.Width / 2; hitBox.Y -= hitBox.Height / 2; } }
        private Rectangle hitBox;
        public bool alive;
        public Vector2 position;
        public Vector2 direction;
        public float rotation { get { return (float)Math.Atan2(direction.Y, direction.X); } }
        public float radius { get { return Math.Max(HitBox.Width, HitBox.Height) / 2; } }

        // se sprite for null, o objeto ficará sem sourceRectangle, origin e hitbox
        public EngineObject(Texture2D sprite)
        {
            this.sprite = sprite;
            direction = Direction.Right;
            position = Vector2.Zero;
            alive = true;
            scale = 1.0f;
            if (sprite != null) sourceRectangle = new Rectangle(0, 0, sprite.Width, sprite.Height);
            HitBox = sourceRectangle;
            origin = new Vector2(sourceRectangle.Width / 2, sourceRectangle.Height / 2);
        }

        public abstract void Update(GameTime gameTime);

        public virtual void Draw(SpriteBatch spriteBatch)
        {
            if (alive)
                spriteBatch.Draw(sprite, position, sourceRectangle, Color.White, rotation, origin, scale, SpriteEffects.None, 0);
        }

        public virtual bool Collision(EngineObject target)
        {
            return HitBox.Intersects(target.HitBox);
        }

        /// <summary>
        /// Corrige o posicionamento deste objeto a partir da colisão com outro objeto.
        /// </summary>
        /// <param name="obj">Outro objeto a ser comparado.</param>
        /// <returns>Retorna verdadeiro caso tenha alterado a posição deste objeto.</returns>
        public virtual bool FixPositionByCollisionWith(EngineObject obj)
        {
            // pega os borders deste obj
            int up = HitBox.Y;
            int down = HitBox.Y + HitBox.Height;
            int left = HitBox.X;
            int right = HitBox.X + HitBox.Width;

            // pega os borders do outro obj
            int objUp = obj.HitBox.Y;
            int objDown = obj.HitBox.Y + obj.HitBox.Height;
            int objLeft = obj.HitBox.X;
            int objRight = obj.HitBox.X + obj.HitBox.Width;

            // pega os diferenciais de cada borda
            // d = delta { up, down, left, right}
            int[] d = { objDown - up, down - objUp, objRight - left, right - objLeft };

            // procura qual diferencial é menor (e maior que zero)
            int iMin = -1;
            for (int i = 0; i < 4; ++i)
            {
                if (d[i] < 0) continue;
                if (iMin == -1 || d[i] < d[iMin]) 
                    iMin = i;
            }

            // corrige a posição utilizando o menor diferencial
            switch (iMin)
            {
                case -1: return false; // quer dizer que na verdade não estão colidindo, então não precisa corrigir a posição
                case 0: position.Y += d[iMin]; break;
                case 1: position.Y -= d[iMin]; break;
                case 2: position.X += d[iMin]; break;
                case 3: position.X -= d[iMin]; break;
            }

            return true;
        }
    }
}